package com.onpositive.knowledge.model.values;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import com.onpositive.semantic.model.api.property.IProperty;

public abstract class ModifyingValueTraverser {

	protected Class<?>[] targetClasses = createTargetClasses();
	protected Class<?>[] necessaryClasses = createNecessaryClasses();

	
	public ModifyingValueTraverser( Class<?>[] tc , Class<?>[] nc ) {
		super();
		this.targetClasses = tc ;
		this.necessaryClasses = nc ;
	}
	
	public ModifyingValueTraverser() {
		super();
	}
	
	public Object iterateValues(
			IProperty prop,
			Object val,
			boolean _hasNecessaryClasses,
			Map<String,Object> metaInfo,
			int index,
			boolean cameFromCollection )
	{
		if( isTargetClass(val) || ( _hasNecessaryClasses && (!(val instanceof IHasValue) ) ) )
			return process( prop, val , metaInfo, index, cameFromCollection ) ;
		
		if(!( val instanceof IHasValue  )){
			
			if( val instanceof Collection<?> )				
				return processCollection( prop, _hasNecessaryClasses, metaInfo, index, (Collection<?>)val );
			
			else if( val instanceof Object[] )
			{
				Class<? extends Object> compType = val.getClass().getComponentType() ;
				List<? extends Object> originalLst = Arrays.asList( (Object[])val ) ;				
				Object newListObject = processCollection(prop, _hasNecessaryClasses, metaInfo, index, originalLst) ;
				
				if( newListObject == null || !(newListObject instanceof List) )
					return null ;
				
				List<? extends Object> newList = (List<?>) newListObject ;
				
				
				try{
					Object[] res = (Object[]) Array.newInstance( compType, newList.size() ) ;
					int i = 0 ;
					for( Object obj : newList )
						res[i++] = obj ;
					
					return res ;
				}
				catch( Exception e ){
					e.printStackTrace() ;
					return null ;
				}
			}
		}
		
		boolean hasNecessaryClasses = false ;
		
		if ( val instanceof IHasValue  )
		{
			hasNecessaryClasses = hasNecessaryClasses || isNecessaryClass(val) ;
					
			Object valObj = ((IHasValue)val).value() ;
			Object newValObj = iterateValues( prop, valObj, hasNecessaryClasses, metaInfo, index, false ) ;
			if( newValObj == null )
				return null ;
			
			return ((IHasValue)val).clone(newValObj, null) ;			
		}
		
		return null ;
	}

	private Object processCollection(
			IProperty prop,
			boolean _hasNecessaryClasses,
			Map<String, Object> meta,
			int index,
			Collection<?> originalLst )
	{
		ArrayList<Object> newLst = new ArrayList<Object>() ;
		boolean gotChange = false ;
		
		for( Object o : originalLst )
		{
			Object newObj = iterateValues( prop, o, _hasNecessaryClasses, meta, index, true) ;
			if( newObj == null )
				newLst.add(o) ;						
			else{
				if( newObj instanceof Collection )
					newLst.addAll( (Collection<?>)newObj ) ;
				else if( newObj instanceof Object[] )
					newLst.addAll( Arrays.asList( (Object[])newObj ) ) ;
				else
					newLst.add(newObj);
				
				gotChange = true ;
			}
		}
		return gotChange ? newLst : null ;
	}

	private boolean isNecessaryClass(Object val) {
		boolean hasNecessaryClass = necessaryClasses.length == 0 ;
		for( Class<?> clazz : necessaryClasses )
			hasNecessaryClass = hasNecessaryClass || clazz.isInstance(val) ;
				
		return hasNecessaryClass ;
	}

	private boolean isTargetClass(Object val) {
		boolean isTargetClass = false ;
		for( Class<?> clazz : targetClasses )
			if( clazz.isInstance(val) )
			{
				isTargetClass = true ;
				break ;
			}
		return isTargetClass;
	}

	protected abstract Object process( IProperty prop, Object val, Map<String,Object> meta, int index, boolean cameFromCollection);

	protected abstract Class<?>[] createNecessaryClasses();

	protected abstract Class<?>[] createTargetClasses();

}
